﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PGMHelper;
using System.Windows.Forms.DataVisualization.Charting;

namespace Spectrum
{
    [JsonObject(MemberSerialization.OptIn)]
    /// <summary>
    /// 反应谱分析结果
    /// </summary>
    public class SpectSet
    {
        #region Variables

        [JsonProperty]
        /// <summary>
        /// 反应谱分析结果
        /// </summary>
        private List<SpectParam> Results { set; get; }

        /// <summary>
        /// 外部激励
        /// </summary>
        private List<AccelParam> Accel { set; get; }

        /// <summary>
        /// 外部激励Y
        /// </summary>
        private List<AccelParam> AccelY { set; get; }

        /// <summary>
        /// 响应列表
        /// </summary>
        private List<ResponseSet> ResponseList { set; get; }

        /// <summary>
        /// 周期长度
        /// </summary>
        public float PeriodLength { set; get; }

        /// <summary>
        /// 周期增量
        /// </summary>
        public float DeltaPeriod { set; get; }

        /// <summary>
        /// 调整系数
        /// </summary>
        protected double PGAMultiplier { set; get; }

        /// <summary>
        /// 阻尼比
        /// 人工波生成必须的变量
        /// </summary>
        protected double DampingRatio { set; get; }

        #endregion

        #region Properties

        /// <summary>
        /// 加速度谱最大值
        /// </summary>
        public float AfaMax
        {
            get
            {
                return this.GetMaxValue("Afa");
            }
        }

        /// <summary>
        /// 速度谱最大值
        /// </summary>
        public float VelocityMax
        {
            get
            {
                return this.GetMaxValue("Velocity");
            }
        }

        /// <summary>
        /// 位移谱最大值
        /// </summary>
        public float DispMax
        {
            get
            {
                return this.GetMaxValue("Disp");
            }
        }

        /// <summary>
        /// 特征周期求解
        /// </summary>
        public float Tg
        {
            get
            {
                //有效峰值速度Vep 相对速度谱在0.5s~2s的平均值与2.5的比值
                //获得0.5s~2s的值
                var VList = (from param in this.Results
                             where param.Period >= 0.5 && param.Period <= 2
                             select param.Velocity);
                //有效峰值速度Vep
                var Vep = VList.Average() / 2.5;
                //有效峰值加速度AEP 加速度反应谱在0.1s~0.5s的平均值与2.5的比值
                //获得0.1s~0.5s的值
                var AList = (from param in this.Results
                             where param.Period >= 0.1 && param.Period <= 0.5
                             select param.Afa);
                //有效峰值加速度AEP
                var Aep = AList.Average() / 2.5;
                //求解特征周期
                return (float)(2 * Math.PI * Vep / Aep);
            }
        }

        /// <summary>
        /// 有效峰值加速度
        /// </summary>
        public float EPA
        {
            get
            {
                //有效峰值加速度EPA 加速度反应谱在0.15s~0.25s的平均值与2.25的比值
                var AList = (from param in this.Results
                             where param.Period >= 0.15 && param.Period <= 0.25
                             select param.Afa);
                return AList.Average() / 2.25F;
            }
        }

        /// <summary>
        /// 反应谱数据点数量
        /// </summary>
        public int Count
        {
            get
            {
                return this.Results.Count;
            }
        }

        /// <summary>
        /// 正则化
        /// </summary>
        /// <returns></returns>
        public virtual void Regularization()
        {
            //反应谱折减
            return;
        }

        /// <summary>
        /// 获得阻尼比
        /// </summary>
        /// <returns></returns>
        public double GetCodeDampRatio()
        {
            return this.DampingRatio;
        }

        /// <summary>
        /// 根据周期计算影响系数
        /// </summary>
        /// <param name="period"></param>
        /// <returns></returns>
        public virtual double GetAfaByPeriod(double period)
        {
            //根据周期寻找最近的谱值
            var spectList = this.Results.Find("Period", period, this.DeltaPeriod / 2);
            //是否存在多个值
            if (spectList.Count == 0)
            {
                MessageBoxExtern.Error(string.Format("目标谱周期{0}对应谱值不存在", period));
                return 0;
            }
            //仅有一个值
            else if (spectList.Count == 1) return spectList.First().Afa;
            //仅有两个值
            else if (spectList.Count == 2)
            {
                return spectList.First().Interpolation(spectList.Last(), (float)period).Afa;
            }
            else
            {
                var valueList = new List<double>();
                for (int i = 0; i < spectList.Count - 1; i++)
                    valueList.Add(spectList[i].Interpolation(spectList[i + 1], (float)period).Afa);
                return valueList.Average();
            }
        }

        /// <summary>
        /// 折减到1PGA所采用的折减系数
        /// </summary>
        /// <returns></returns>
        public virtual double GetReductionFactor()
        {
            return 1;
        }

        /// <summary>
        /// 获得峰值加速度
        /// </summary>
        /// <returns></returns>
        public double GetPGA()
        {
            return 9.8E2 / this.GetReductionFactor();
        }

        /// <summary>
        /// 修改PGA值
        /// </summary>
        /// <param name="pga"></param>
        public void SetPGA(double targetPGA)
        {
            this.PGAMultiplier *= targetPGA / this.GetPGA();
        }

        /// <summary>
        /// 获得反应谱对象
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public SpectParam this[int index]
        {
            get
            {
                return this.Results[index];
            }
        }

        #endregion

        /// <summary>
        /// Dt上限值
        /// </summary>
        private int MinTDtFactor { set; get; }

        /// <summary>
        /// 添加数据
        /// </summary>
        /// <param name="param"></param>
        public void Add(SpectParam param)
        {
            this.Results.Add(param);
        }

        /// <summary>
        /// 添加数据
        /// </summary>
        /// <param name="period"></param>
        /// <param name="Afa"></param>
        public void Add(float period, float afa)
        {
            this.Add(new SpectParam
            {
                Period = period,
                Afa = afa,
            });
        }

        /// <summary>
        /// 清空数据
        /// </summary>
        public void Clear()
        {
            this.Results.Clear();
        }

        /// <summary>
        /// 反应谱调幅度
        /// </summary>
        /// <param name="factor"></param>
        public void SpectrumAmplitude(float factor)
        {
            this.Results.ForEach(param => param.SpectrumAmplitude(factor));
        }

        #region GetMethod

        /// <summary>
        /// 获得反应谱数据
        /// </summary>
        /// <returns></returns>
        public GridViewDatas GetData()
        {
            //初始化数据
            var data = new GridViewDatas("反应谱数据");
            //获得周期列表
            var periodList = (from param in this.Results select param.Period).ToList();
            //获得周期列
            data.Add(new GridViewColumn(periodList, demical: 2, titleName: "周期", isreadOnly: true, width: 100));
            //添加国家反应谱
            data.Add(new GridViewColumn(this.GetASpectrum(), demical: 6, titleName: "谱值", isreadOnly: true, width: 100));
            //返回数据
            return data;
        }

        /// <summary>
        /// 获得最大值
        /// </summary>
        /// <param name="propertyName"></param>
        private float GetMaxValue(string propertyName)
        {
            //获得值列表
            var valueList = this.Results.GetPropertyList(propertyName).ConvertAll(f => (float)f);
            //返回最大值
            return valueList.Max();
        }

        /// <summary>
        /// 获得最后的加速度对象
        /// </summary>
        /// <returns></returns>
        public SpectParam Last()
        {
            return this.Results.Last();
        }

        /// <summary>
        /// 获得加速度反应谱数据
        /// </summary>
        /// <returns></returns>
        public List<float> GetASpectrum()
        {
            return (from param in this.Results select param.Afa).ToList();
        }

        /// <summary>
        /// 获得值列表
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public List<float> GetValueList(string propertyName)
        {
            return this.Results.GetPropertyList(propertyName).ConvertAll(f => (float)f);
        }

        /// <summary>
        /// 设定反应谱
        /// </summary>
        /// <param name="paramList"></param>
        /// <param name="dampingRatio"></param>
        /// <param name="isPeseudo"></param>
        public SpectSet GetSpectrum(List<float> paramList, float dampingRatio, bool isPeseudo)
        {
            //确定分析步数目
            int stepNumber = (int)(this.PeriodLength / this.DeltaPeriod) + 1;
            //初始化
            this.Results = new List<SpectParam>(stepNumber - 1);
            //遍历每个分析步
            for (int step = 1; step < stepNumber; step++)
            {
                //确定周期
                float period = this.DeltaPeriod * step;
                //构造
                var response = new ResponseSet(this.Accel, paramList, period, dampingRatio);
                //获得反应谱值
                var spect = new SpectParam(period, isPeseudo, response);
                //是否存在Y向时程
                if (this.AccelY.Count != 0)
                {
                    //构造Y向时程响应
                    var responseY = new ResponseSet(this.AccelY, paramList, period, dampingRatio);
                    //获得SRSS响应
                    var responseSRSS = responseY.GetSRSSResponse(response);
                    //获得影响系数最大值
                    spect.SetSpectrum(responseY, isPeseudo);
                    spect.SetSpectrum(responseSRSS, isPeseudo);
                }
                //获得最大影响系数
                this.Results.Add(spect);
            }
            return this;
        }

        /// <summary>
        /// 求解固定周期的反应谱
        /// </summary>
        /// <param name="periodList"></param>
        /// <param name="dampingRatio"></param>
        /// <param name="integrateDt"></param>
        /// <returns></returns>
        public SpectSet GetSpectrumFast(List<double> periodList, float dampingRatio, float integrateDt)
        {
            //初始化
            this.Results = new List<SpectParam>(periodList.Count);
            this.ResponseList = new List<ResponseSet>(periodList.Count);
            //遍历周期
            periodList.ForEach(period =>
            {
                //构造
                var response = new ResponseSet(this.Accel, (float)period, dampingRatio, integrateDt);
                //获得反应谱值
                var spect = new SpectParam((float)period, true, response);
                //获得响应
                this.ResponseList.Add(response);
                //获得最大影响系数
                this.Results.Add(spect);
            });
            return this;
        }

        /// <summary>
        /// 设定反应谱Newmark
        /// </summary>
        /// <param name="paramList"></param>
        /// <param name="dampingRatio"></param>
        /// <param name="isPeseudo"></param>
        /// <param name="integrateDt"></param>
        /// <returns></returns>
        public SpectSet GetSpectrumByNewmark(List<float> paramList, float dampingRatio, bool isPeseudo, float integrateDt)
        {
            //确定分析步数目
            int stepNumber = (int)(this.PeriodLength / this.DeltaPeriod) + 1;
            //初始化
            this.Results = new List<SpectParam>(stepNumber - 1);
            this.ResponseList = new List<ResponseSet>(stepNumber - 1);
            //遍历每个分析步
            for (int step = 1; step < stepNumber; step++)
            {
                //确定周期
                float period = this.DeltaPeriod * step;
                //确定最大积分步长
                var maxDt = this.MinTDtFactor == -1 ? integrateDt : period / this.MinTDtFactor;
                //获得加速度时程
                var accelList = this.GetAccelList(this.Accel, integrateDt, maxDt);
                //计算Newmark参数
                var newmarkParams = integrateDt <= maxDt ? paramList :
                    SpectrumMethod.GetNewMarkParamList(0.25F, 0.5F, maxDt);
                //构造
                var response = new ResponseSet(accelList, newmarkParams, period, dampingRatio);
                //获得反应谱值
                var spect = new SpectParam(period, isPeseudo, response);
                //获得响应
                this.ResponseList.Add(response);
                //获得最大影响系数
                this.Results.Add(spect);
            }
            return this;
        }

        /// <summary>
        /// 设定反应谱
        /// 分段解析法
        /// </summary>
        /// <param name="dampingRatio"></param>
        /// <param name="integrateDt"></param>
        /// <returns></returns>
        public SpectSet GetSpectrumByExcitation(float dampingRatio, float integrateDt)
        {
            //确定分析步数目
            int stepNumber = (int)(this.PeriodLength / this.DeltaPeriod) + 1;
            //初始化
            this.Results = new List<SpectParam>(stepNumber - 1);
            this.ResponseList = new List<ResponseSet>(stepNumber - 1);
            //遍历每个分析步
            for (int step = 1; step < stepNumber; step++)
            {
                //确定周期
                float period = this.DeltaPeriod * step;
                //确定最大积分步长
                var maxDt = this.MinTDtFactor == -1 ? integrateDt : period / this.MinTDtFactor;
                //获得加速度时程
                var accelList = this.GetAccelList(this.Accel, integrateDt, maxDt);
                //构造
                var response = new ResponseSet(accelList, period,
                    dampingRatio, Math.Min(maxDt, integrateDt));
                //获得反应谱值
                var spect = new SpectParam(period, true, response);
                //获得响应
                this.ResponseList.Add(response);
                //获得最大影响系数
                this.Results.Add(spect);
            }
            return this;
        }

        /// <summary>
        /// 获得加速度时程曲线
        /// </summary>
        /// <param name="accelList"></param>
        /// <param name="dt"></param>
        /// <returns></returns>
        private List<AccelParam> GetAccelList(List<AccelParam> accelList, float integrateDt, float dt)
        {
            //满足要求
            if (integrateDt <= dt) return accelList;
            //重构加速度时程曲线
            var accel = new AccelSet(this.Accel);
            //稀疏时程曲线
            return accel.GetSpraseAccelHist(dt, -1);
        }

        /// <summary>
        /// 获得反应谱数据
        /// </summary>
        /// <param name="period"></param>
        /// <returns></returns>
        public SpectParam GetSpectrumParam(float period)
        {
            //根据周期寻找最近的谱值
            var spectList = this.Results.Find("Period", period, this.DeltaPeriod / 10);
            //是否存在多个值
            if (spectList.Count == 0)
            {
                //MessageBoxExtern.Error(string.Format("目标谱周期{0}对应谱值不存在", period));
                return null;
            }
            else if (spectList.Count > 1)
                MessageBoxExtern.Error(string.Format("目标谱周期{0}存在相似谱值", period));
            return spectList.First();
        }

        /// <summary>
        /// 获得周期对应的激励响应
        /// </summary>
        /// <param name="period"></param>
        /// <returns></returns>
        public ResponseSet GetResponseSet(float period)
        {
            //根据周期寻找最近的谱值
            var responses = this.ResponseList.Find("Period", period, this.DeltaPeriod / 10);
            //是否存在多个值
            if (responses.Count == 0)
            {
                //MessageBoxExtern.Error(string.Format("目标谱周期{0}对应谱值不存在", period));
                return null;
            }
            else if (responses.Count > 1)
                MessageBoxExtern.Error(string.Format("目标谱周期{0}存在相似谱值", period));
            return responses.First();
        }

        /// <summary>
        /// 获得分析结果
        /// </summary>
        /// <param name="paramList"></param>
        /// <param name="dampingRatio"></param>
        /// <param name="isPeseudo"></param>
        /// <returns></returns>
        public List<SpectParam> GetResults(List<float> paramList, float dampingRatio, bool isPeseudo)
        {
            //设定反应谱
            this.GetSpectrum(paramList, dampingRatio, isPeseudo);
            //返回分析结果
            return this.Results;
        }

        #endregion

        /// <summary>
        /// 绘制时程曲线
        /// </summary>
        /// <param name="chart"></param>
        public void ToChart(Chart chart, bool isOrigin)
        {
            if (!isOrigin) chart.Series.Clear();
            var name = isOrigin ? "规范反应谱" : "人工波反应谱";
            chart.AddSeries(name, SeriesChartType.Line,
                isOrigin ? System.Drawing.Color.Red : System.Drawing.Color.Black, 3);
            chart.Series[name].Points.DataBindXY(this.GetValueList("Period"),
               this.GetValueList("Afa"));
        }

        /// <summary>
        /// 内存释放
        /// </summary>
        public void TrimExcess()
        {
            this.AccelY.Clear();
            this.ResponseList.Clear();
            this.AccelY.TrimExcess();
            this.ResponseList.TrimExcess();
        }

        /// <summary>
        /// 空的构造函数
        /// </summary>
        public SpectSet()
        {
            this.Results = new List<SpectParam>();
            this.Accel = new List<AccelParam>();
            this.AccelY = new List<AccelParam>();
            this.PGAMultiplier = 1;
        }

        /// <summary>
        /// 主构造函数
        /// </summary>
        /// <param name="accel"></param>
        /// <param name="periodLength"></param>
        /// <param name="deltaPeriod"></param>
        public SpectSet(List<AccelParam> accel, List<AccelParam> accelY, float periodLength, float deltaPeriod)
        {
            this.Accel = accel;
            this.AccelY = accelY;
            this.PeriodLength = periodLength;
            this.DeltaPeriod = deltaPeriod;
            this.PGAMultiplier = 1;
        }

        /// <summary>
        /// 主构造函数
        /// </summary>
        /// <param name="accel"></param>
        /// <param name="periodLength"></param>
        /// <param name="deltaPeriod"></param>
        public SpectSet(List<AccelParam> accel, float periodLength, float deltaPeriod, int minTDtFactor)
        {
            this.Accel = accel;
            this.AccelY = new List<AccelParam>(); ;
            this.PeriodLength = periodLength;
            this.DeltaPeriod = deltaPeriod;
            this.MinTDtFactor = minTDtFactor;
            this.PGAMultiplier = 1;
        }
    }
}
